package org.example.model;


import io.netty.util.collection.LongObjectHashMap;
import org.example.command.CmdResultCode;
import org.example.engine.RbCmd;
import org.example.order.OrderDirection;

import java.util.*;

public class OrderBookImpl implements OrderBook{

    private int code;

    public OrderBookImpl(int code) {
        this.code = code;
    }

    private final NavigableMap<Long, OrderBucket> sellBuckets = new TreeMap<>();
    private final NavigableMap<Long, OrderBucket> buyBuckets = new TreeMap<>(Collections.reverseOrder());

    private final LongObjectHashMap<Order> oidMap = new LongObjectHashMap<>();
    @Override
    public CmdResultCode newOrder(RbCmd cmd) {
        if(oidMap.containsKey(cmd.oid)){
            return CmdResultCode.DUPLICATE_ORDER_ID;
        }

        NavigableMap<Long, OrderBucket> buckets = (cmd.isSell() ? buyBuckets : sellBuckets)
                .headMap(cmd.price, true);

        long matchedVolume = preMatch(cmd, buckets);
        if(matchedVolume == cmd.volume) {
            return CmdResultCode.SUCCESS;
        }
        Order order = new Order();
        order.setMid(cmd.getMid());
        order.setUid(cmd.getUid());
        order.setCode(cmd.getCode());
        order.setPrice(cmd.price);
        order.setVolumn(cmd.getVolume());
        order.setDirection(cmd.getDirection());
        order.addCompletedVolumn(matchedVolume);
        order.setOrderId(cmd.getOid());
        order.setTimestamp(cmd.timestamp);

        OrderBucket bucket = (cmd.isSell() ? sellBuckets : buyBuckets)
                .computeIfAbsent(cmd.price, p -> {
                    final  OrderBucket b = new OrderBucketImpl();
                    b.setPrice(p);
                    return b;
                });
        bucket.put(order);
        return CmdResultCode.SUCCESS;
    }

    private long preMatch(RbCmd cmd, NavigableMap<Long, OrderBucket> buckets) {
        int matchedVolum = 0;
        if(buckets.size() == 0) {
            return matchedVolum;
        }
        List<Long> emptyBuckets = new ArrayList<>(5);

        for(OrderBucket bucket : buckets.values()) {
            matchedVolum += bucket.match(cmd.volume - matchedVolum, cmd,
                    order -> oidMap.remove(order.getOrderId()));
            if(bucket.isEmpty()) {
                emptyBuckets.add(bucket.getPrice());
            }
            if(matchedVolum == cmd.volume){
                break;
            }
        }
        emptyBuckets.forEach(buckets::remove);
        return matchedVolum;
    }

    @Override
    public CmdResultCode cancleOrder(RbCmd cmd) {
        //1. 从缓存中移除代码
        Order order = oidMap.get(cmd.getOid());
        if(order == null) {
            return CmdResultCode.INVALID_ORDER_ID;
        }
        this.oidMap.remove(order.getOrderId());

        //2. 从orderBucket中移除委托
        final NavigableMap<Long, OrderBucket> buckets = order.getDirection() == OrderDirection.SELL ? sellBuckets : buyBuckets;
        OrderBucket orderBucket = buckets.get(order.getPrice());
        orderBucket.remove(order.getOrderId());
        if(orderBucket.isEmpty()) {
            buckets.remove(order.getPrice());
        }

        //3. 发送撤单Event
        return CmdResultCode.SUCCESS;
    }
}
